# app.py
from flask import Flask, render_template, request, jsonify, send_from_directory, abort
from flask_socketio import SocketIO
from werkzeug.utils import secure_filename
from pathlib import Path
import os
import time
import traceback

# 基础配置
BASE_DIR = Path(__file__).resolve().parent
UPLOAD_DIR = BASE_DIR / "uploads"
UPLOAD_DIR.mkdir(parents=True, exist_ok=True)

# Flask + SocketIO
app = Flask(__name__, template_folder=str(BASE_DIR / "templates"))
app.config["SECRET_KEY"] = "dev-secret-key"  # 请在生产环境中修改
app.config["MAX_CONTENT_LENGTH"] = 1024 * 1024 * 1024  # 1GB，可自行调整

# 使用 eventlet/gevent 更佳，这里直接用默认（如已安装 eventlet，将自动优先）
socketio = SocketIO(app, cors_allowed_origins="*")


def unique_filename(folder: Path, filename: str) -> str:
    """对重复文件名自动加 (1), (2) ... 后缀避免覆盖"""
    name = filename
    base, ext = os.path.splitext(filename)
    i = 1
    while (folder / name).exists():
        name = f"{base}({i}){ext}"
        i += 1
    return name


def list_files():
    """列出上传目录中的文件信息"""
    rows = []
    for entry in sorted(UPLOAD_DIR.glob("*")):
        if entry.is_file():
            stat = entry.stat()
            rows.append({
                "name": entry.name,
                "size": stat.st_size,
                "mtime": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(stat.st_mtime))
            })
    return rows


@app.route("/")
def index():
    return render_template("index.html")


@app.get("/files")
def files_list():
    return jsonify({"ok": True, "files": list_files()})


@app.post("/upload")
def upload_file():
    """
    上传接口。前端通过 FormData 传：
    - file: 文件内容
    - sid: socket.io 的 socket.id，用于指定推送对象
    - upload_id: 客户端生成的本次上传唯一ID（前端用于对应进度条）
    - filename: 原始文件名（可选），会被 secure_filename 处理
    - filesize: 原始文件字节大小（可选）
    """
    try:
        if "file" not in request.files:
            return jsonify({"ok": False, "msg": "缺少文件字段 file"}), 400

        f = request.files["file"]
        raw_name = request.form.get("filename") or f.filename or "uploaded"
        safe_name = secure_filename(raw_name) or "uploaded"
        safe_name = unique_filename(UPLOAD_DIR, safe_name)

        sid = request.form.get("sid")
        upload_id = request.form.get("upload_id") or safe_name
        try:
            total = int(request.form.get("filesize", "0"))
        except Exception:
            total = 0

        dest_path = UPLOAD_DIR / safe_name
        bytes_written = 0

        # 逐块写入并推送进度
        with open(dest_path, "wb") as out:
            chunk_size = 8192
            stream = f.stream
            while True:
                chunk = stream.read(chunk_size)
                if not chunk:
                    break
                out.write(chunk)
                bytes_written += len(chunk)

                if sid:
                    socketio.emit(
                        "upload_progress",
                        {
                            "upload_id": upload_id,
                            "filename": safe_name,
                            "bytes": bytes_written,
                            "total": total
                        },
                        to=sid
                    )

        # 上传完成，广播文件列表已更新
        socketio.emit("file_list_updated", {"files": list_files()})
        return jsonify({"ok": True, "filename": safe_name})

    except Exception as e:
        traceback.print_exc()
        sid = request.form.get("sid")
        upload_id = request.form.get("upload_id")
        if sid and upload_id:
            socketio.emit("upload_error", {"upload_id": upload_id, "error": str(e)}, to=sid)
        return jsonify({"ok": False, "msg": str(e)}), 500


@app.get("/download/<path:filename>")
def download(filename):
    # 仅从 UPLOAD_DIR 中下载，防止路径遍历
    safe_name = secure_filename(filename)
    file_path = UPLOAD_DIR / safe_name
    if not file_path.exists() or not file_path.is_file():
        abort(404)
    return send_from_directory(UPLOAD_DIR, safe_name, as_attachment=True)


@socketio.on("connect")
def on_connect():
    # 可选：欢迎消息
    # from flask import request
    # print("Client connected:", request.sid)
    pass


@socketio.on("disconnect")
def on_disconnect():
    # print("Client disconnected")
    pass


if __name__ == "__main__":
    # 如安装了 eventlet： pip install eventlet
    # socketio.run(app, host="0.0.0.0", port=5000, debug=True)
    socketio.run(app, host="127.0.0.1", port=5000, debug=True)